<?php

/**
 * @author 		Samuel Carlier
 * @package 	P3Base_Socket
 */
abstract class P3Base_Socket_Client {

	/**
	 * control linefeed
	 */
	const CRLF = "\r\n";

	/**
	 * sets timeout for connection to SMTP server
	 */
	const CONNECTION_TIMEOUT = 10;

	/**
	 * socket resource
	 */
	protected $_socket = null;

	/**
	 * @var int
	 */
	protected $_port = null;

	/**
	 * @var int
	 */
	protected $_server = null;

	/**
	 * @var array
	 */
	protected $_timeout = null;

	/**
	 * @var P3Base_Base_Debugger
	 */
	protected $_debugger = null;
	
	/**
	 * @var mixed
	 */
	protected $_secure;

	/**
	 * create a connection with the smtp server
	 *
	 * @param string $server
	 * @param int	 $port
	 * @param bool	 $secure
	 */
	public function __construct($server='localhost', $port=25, $secure=false) {
		$this->_server = $server;
		$this->_port   = $port;
		$this->_secure = in_array($secure, array('tls', 'ssl')) ? $secure : false;
	}

	/**
	 * Close the connection
	 */
	public function close() {
		if(is_null($this->_socket)) {
			return;	
		}

		@fclose($this->_socket);
		$this->_socket = null;
	}

	/**
	 * sets the timeout limit for reading and writeing to the connected socket
	 *
	 * @param int $seconds
	 * @param int $microseconds
	 */
	public function setTimeout($seconds=3, $microseconds=0) {
		if(is_null($this->_socket)) {
			$this->_timeout = array('seconds' => $seconds, 'micro' => $microseconds);
			return;	
		}

        if(substr(PHP_OS, 0, 3) == "WIN") {
			throw new P3Base_Socket_Exception(P3Base_Language_Translator::get('not_implemented'));
        }

		$timeout = @stream_set_timeout($this->_socket, $seconds, $microseconds);
		if($timeout === false) {
			throw new P3Base_Socket_Exception(P3Base_Language_Translator::get('timeout_failed'));
		}
	}

	/**
	 * connect to the server if there was not a active socket found
	 */
	protected function _connect() {
		if(!is_null($this->_socket)) {
			return;	
		}

		$server = $this->_server;
		$port 	= $this->_port;
		
		if(strpos($server, ';') !== false) {
			$servers = explode(';', $server);

		} else {
			$servers[] = $server;
		}

		while(list(,$address) = each($servers)) {	
			$this->_socket = @stream_socket_client($address . ':' . $port, $errno, $errstr, self::CONNECTION_TIMEOUT);

			if($this->_socket !== false) {	
				break;
			}
		}

		if($this->_socket === false) {
			throw new P3Base_Socket_Exception(sprintf(P3Base_Language_Translator::get('connection_error'),  $server . ':' . $port));
		}
		
		if($this->_secure === 'ssl') {
			$this->_enable_crypto(STREAM_CRYPTO_METHOD_SSLv23_CLIENT);
		}

		//check if there is still a timeout to do
		if(!is_null($this->_timeout)) {
			$this->setTimeout($this->_timeout['seconds'], $this->_timeout['micro']);
			$this->_timeout = null;
		}
	}
	
	/**
	 * @param int $crypto
	 */
	protected function _enable_crypto($crypto) {
		stream_socket_enable_crypto($this->_socket, true, $crypto);
	}

	/**
	 * @param P3Base_Base_Debugger $debugger
	 */
	public function setDebugger(P3Base_Base_Debugger $debugger) {
		$this->_debugger = $debugger;
	}
	
	/**
	 * @param string $content
	 * @param bool 	 $line_ending
	 */
	protected function _write($content, $line_ending=true) {
		if($line_ending) {
			$content = $content . self::CRLF;
		}

		if(!is_resource($this->_socket)) {
			throw new P3Base_Socket_Exception(P3Base_Language_Translator::get('invalid_connection'));
		}

		if(!is_null($this->_debugger) && !$this->_debugger->isMode(P3Base_Base_Debugger::MODE_DISABLED)
									  &&  $this->_debugger->isType(P3Base_Base_Debugger::TYPE_WRITE)) {

			$this->_debugger->appendDebug('>> ' . $content);
		}

		$write = @fwrite($this->_socket, $content, strlen($content));

		if(!$write) {
			$this->close();
			throw new P3Base_Mailer_Handler_Exception(P3Base_Language_Translator::get('write_error'));
		}
	}
	
	/**
	 * read content returned by the connected socket
	 * with a maximum lenght
	 *
	 * @param  int $lenght
	 * @return string
	 */
	protected function _read($lenght=512) {
		$read = @fgets($this->_socket, $lenght);
		if($read === false) {
			$this->close();
			throw new P3Base_Socket_Exception(P3Base_Language_Translator::get('read_error'));
		}

		if(!is_null($this->_debugger) && !$this->_debugger->isMode(P3Base_Base_Debugger::MODE_DISABLED)
									  &&  $this->_debugger->isType(P3Base_Base_Debugger::TYPE_READ)) {

			$this->_debugger->appendDebug('<< ' . $read);
		}

		return $read;	
	}
}

?>
